package cn.wen233.srpc.core.spring.client;

import cn.wen233.srpc.basic.model.ServerConfig;
import cn.wen233.srpc.basic.model.ServerInstance;
import cn.wen233.srpc.basic.utils.Pair;
import cn.wen233.srpc.core.holder.ClientServiceHolder;
import cn.wen233.srpc.core.service.Config;
import cn.wen233.srpc.core.service.SunglowReference;
import cn.wen233.srpc.core.service.factory.ReferenceProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * SunglowReference注解后置处理器
 *
 * @author wenei
 * @date 2021-12-27 20:56
 */
public class ReferencePostProcessor implements BeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(ReferencePostProcessor.class);

    private static final Map<Class<?>, List<Pair<Object, Field>>> fieldMap = new HashMap<>();

    private static void fieldMapPut(Class<?> tClass, Pair<Object, Field> pair) {
        List<Pair<Object, Field>> pairs = fieldMap.computeIfAbsent(tClass, k -> new ArrayList<>());
        pairs.add(pair);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            SunglowReference sunglowReference = field.getDeclaredAnnotation(SunglowReference.class);
            if (sunglowReference != null) {
                // 定义的类
                Class<?> fieldType = field.getType();
                // 尝试获取Proxy实例
                Object proxy = ClientServiceHolder.getProxy(fieldType);
                if (proxy == null) {
                    Class<?> fallbackFactory = sunglowReference.fallbackFactory();
                    Config[] localConfigs = sunglowReference.value();
                    ServerConfig serverConfig = new ServerConfig();
                    serverConfig.setServiceName(fieldType.getName());
                    serverConfig.setInstances(mapFromConfigs(localConfigs));
                    ClientServiceHolder.putProxy(
                            fieldType,
                            () -> ReferenceProxyFactory.getProxyService(fieldType, serverConfig, fallbackFactory)
                    );
                    proxy = ClientServiceHolder.getProxy(fieldType);
                    log.info("生成{}类型的Client代理实例", fieldType);
                }
                fieldMapPut(fieldType, Pair.of(bean, field));
                injectProxy(bean, field, proxy);
            }
        }
        return bean;
    }

    private List<ServerInstance> mapFromConfigs(Config[] configs) {
        List<ServerInstance> instances = new ArrayList<>();
        for (Config config : configs) {
            ServerInstance instance = new ServerInstance();
            instance.setHost(config.host());
            instance.setPort(config.port());
            instances.add(instance);
        }
        return instances;
    }

    public static void injectProxy(Class<?> tClass, Object proxy) {
        List<Pair<Object, Field>> pairs = fieldMap.get(tClass);
        if (pairs == null) {
            return;
        }
        for (Pair<Object, Field> fieldPair : pairs) {
            log.debug("bean={}，field={} 替换其代理对象 为proxy={}", fieldPair.fst, fieldPair.snd, proxy);
            injectProxy(fieldPair.fst, fieldPair.snd, proxy);
        }
    }

    private static void injectProxy(Object bean, Field field, Object proxy) {
        field.setAccessible(true);
        try {
            field.set(bean, proxy);
        } catch (IllegalAccessException e) {
            log.warn("字段{}注入proxy失败", field);
        }
        field.setAccessible(false);
    }
}
